package com.samehope.ar.util;

import com.alibaba.fastjson.JSON;
import com.samehope.ar.weixin.envir.WeiXinProperties;
import com.samehope.ar.weixin.support.MiniSession;
import com.samehope.ar.weixin.support.PhoneData;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;

import javax.annotation.PostConstruct;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description: 微信小程序帮助类
 * @Author: ZhangLuo
 * @Email: 1946430@qq.com
 */
@Slf4j
@Component
public class WeiXinMiniUtils {

    /**
     * 登录授权信息查询接口 code2session
     */
    private static final String CODE_2_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session";

    private static WeiXinProperties properties;

    @Autowired
    private WeiXinProperties weiXinProperties;

    @PostConstruct
    public void init() {
        properties = weiXinProperties;
    }

    /**
     * 根据code获取小程序session信息
     * 信息字典如下:
     *  -1	    系统繁忙，此时请开发者稍候再试
     *  0	    请求成功
     *  40029	code 无效
     *  45011	频率限制，每个用户每分钟100次
     *  check: https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
     * @return
     */
    public static MiniSession session(String code) {

        Map<String, String> parmas = new HashMap<>();
        parmas.put("appid", properties.getMiniAppId());
        parmas.put("secret", properties.getMiniSecretKey());
        parmas.put("grant_type", "authorization_code");
        parmas.put("js_code", code);

        String response = HttpUtils.httpGet(CODE_2_SESSION_URL, parmas, new HashMap<>());
        MiniSession miniSession = JSON.parseObject(response, MiniSession.class);
        if (miniSession.getErrcode() != null) {
            throw new IllegalArgumentException(miniSession.getErrmsg());
        }
        return miniSession;
    }



    /**
     * 解密手机号信息
     * @param code 前端wx.login后获取到的信息
     * @param encryptedData 前端getPhoneNumber后获取到的信息
     * @param iv 前端getPhoneNumber后获取到的信息
     * check: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html
     * @return
     */
    public static PhoneData decryptPhoneData(String code, String encryptedData, String iv) {

        String decryptData = decryptData(code, encryptedData, iv);
        PhoneData phoneData = JSON.parseObject(decryptData, PhoneData.class);
        if (!properties.getMiniAppId().equals(phoneData.getWatermark().getAppid())) {
            throw new IllegalArgumentException("无效的数据, 解密失败");
        }
        phoneData.setWatermark(null);
        return phoneData;
    }


    /**
     * 解密微信小程序加密数据
     * @param code 前端wx.login后获取的
     * @param encryptedData 前端getPhoneNumber后获取到的信息
     * @param iv 前端getPhoneNumber后获取到的信息
     * @return
     */
    private static String decryptData(String code, String encryptedData, String iv) {
        MiniSession session = session(code);

        // 被加密的数据
        byte[] dataByte = Base64Utils.decodeFromString(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64Utils.decodeFromString(session.getSession_key());
        // 偏移量
        byte[] ivByte = Base64Utils.decodeFromString(iv);
        try {
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");

            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");

            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");

            parameters.init(new IvParameterSpec(ivByte));

            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);

            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                return result;
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

}
